Part 1: stats
install.packages("wordspace") # contains dist.matrix function
also installing the dependencies ‘sparsesvd’, ‘iotools’
trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/sparsesvd_0.2.tgz'
Content type 'application/x-gzip' length 74751 bytes (72 KB)
==================================================
downloaded 72 KB
trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/iotools_0.3-2.tgz'
Content type 'application/x-gzip' length 154104 bytes (150 KB)
==================================================
downloaded 150 KB
trying URL 'https://cran.rstudio.com/bin/macosx/contrib/4.1/wordspace_0.2-6.tgz'
Content type 'application/x-gzip' length 2509428 bytes (2.4 MB)
==================================================
downloaded 2.4 MB
The downloaded binary packages are in
/var/folders/k4/khtkczkd5tn732ftjpwgtr240000gn/T//RtmpHMlGUN/downloaded_packages
library("wordspace")
Loading required package: Matrix
Attaching package: ‘Matrix’
The following object is masked from ‘package:flowCore’:
%&%
dm <- dist.matrix(m, as.dist = TRUE)
Error: vector memory exhausted (limit reached?)
Saved the flowset object I created and load the object here - but I didn’t save the filtered df converted to matrix, which is needed for the stats below
fs <- readRDS("/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/9MBO/prepro_outsjan20-9000cells/FlowSOMin.rds")
# note to Shuming - we should be able to go directly from flowset in preprocessing into this flowset but then the 9 samples (or however many will be in separate frames)
# I don't know where the actual values are located
# skip
#based on flowsom, the optimal k number of clusters = 5 for the sample (n=3000)
metacl <- MetaClustering(fs$map$codes,
"metaClustering_consensus",
max = 15)
unique(metacl[fs$map$mapping[, 1]])
dm = dist.matrix(m, as.dist = TRUE)
Error: vector memory exhausted (limit reached?)
# run the function to get the stats
krange <- 3:15 #range of number of clusters
li = stats(krange) # doesn't contain silhouette
# list of 2
#silhouette score: ranges from -1 to 1
#-1: bad clusters 0: neutral, indifferent 1: good clusters
#plot(krange, type='b', li[[1]][krange], xlab='Number of clusters', ylab='Average Silhouette Scores', frame=TRUE)
#Calinski-Harabasz index:
# the highest value is the optimal number of clusters
plot(krange, type='b', li[[1]][krange], xlab='Number of clusters', ylab='Calinski-Harabasz index', frame=TRUE)
#Davies–Bouldin index: minimum score is zero
#the lowest value is the optimal number of clusters
plot(krange, type='b', li[[2]][krange], xlab='Number of clusters', ylab='Davies–Bouldin index', frame=TRUE)
The Calinski-Harabasz index show that K=8 is best number of clusters. But the Davies–Bouldin index shows lower is better. It is okay up until 9.
Try statistics on other clustering.
ch.pheno.k.50 = calinhara(m,df$phenograph,length((unique(df$phenograph))))
Error in if (cln[i] < 2) cclx <- 0 : argument is of length zero
David Bouldin index for Phenograph
#calculate Davies–Bouldin index
# indiex.list, intracls, intercls
# I'm not sure how this is working but it does run
dbi.phen.k.271 <- clv.Davies.Bouldin(cls.scatt.data(
m,
df$phenograph_clusterk271),
intracls = "average",
intercls = "average"
)
dbi.phen.k.50 <- clv.Davies.Bouldin(cls.scatt.data(
m,
df$phenograph_cluster),
intracls = "average",
intercls = "average"
)
make this calculations on the Seurat clustering
# read in the seurat object with the clustering values
seu <- readRDS("/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/9MBO/prepro_outsjan20-9000cells/SeuratfromFlowsomPheno.rds" )
CH index and DBI from Seurat (Louvain) try one resolution first
dbi.louv.res.1 <- clv.Davies.Bouldin(cls.scatt.data(
m,
seu@meta.data$RNA_snn_res.1),
intracls = "average",
intercls = "average"
)
Error in cls.id.vect.validity(clust, "clust") :
Bad usage: input 'clust' should be vector type.
NA
Calculate all CHI for Louvain
ch_louv <-list()
l.range <- c("RNA_snn_res.0.1","RNA_snn_res.0.25","RNA_snn_res.0.5","RNA_snn_res.0.75","RNA_snn_res.1")
resolution = c(0.1,0.25,0.5,0.75,1)
prefix = "RNA_snn_res."
# loop doesn't work because I can't index the seurat resolutions
ch = calinhara(m,seu@meta.data$RNA_snn_res.0.1,length((unique(seu@meta.data$RNA_snn_res.0.1))))
ch_louv["RNA_snn_res.0.1"] <- ch
ch = calinhara(m,seu@meta.data$RNA_snn_res.0.25,length((unique(seu@meta.data$RNA_snn_res.0.25))))
ch_louv["RNA_snn_res.0.25"] <- ch
ch = calinhara(m,seu@meta.data$RNA_snn_res.0.5,length((unique(seu@meta.data$RNA_snn_res.0.5))))
ch_louv["RNA_snn_res.0.5"] <- ch
ch = calinhara(m,seu@meta.data$RNA_snn_res.0.75,length((unique(seu@meta.data$RNA_snn_res.0.75))))
ch_louv["RNA_snn_res.0.75"] <- ch
ch = calinhara(m,seu@meta.data$RNA_snn_res.1,length((unique(seu@meta.data$RNA_snn_res.1))))
ch_louv["RNA_snn_res.1"] <- ch
Plot the CHI from Louvain clusters

Make a table with the CHI outputs
df.chi <- as.data.frame(Method, CHI.value)
Warning in as.data.frame.vector(x, ..., nm = nm) :
'row.names' is not a character vector of length 7 -- omitting it. Will be an error!
Make another plot
#library(ggplot2)
ggplot(df.chi, aes(x= Method, y= CHI.value)) + geom_point() +
theme(axis.text.x = element_text(angle = 90))

Part 2: plot
#transpose the csv so that seurat object has the right column and row
# the flow intensity values will be input as RNA expression
tm <- t(df2)
rownames(tm) <- colnames(df2)
colnames(tm) <- rownames(df2)
k <- 10 #change number of cluster here
k <- 8 # best number of clusters according to CH index
metaClustering <- (metaClustering_consensus(fs$map$codes,k = k,seed=42)) # flowSOM clustering?
seu <- CreateSeuratObject(tm) # create a seurat object
# check the seurat object by plotting some features
print(colnames(df2))
allAB <- colnames(df2)
VlnPlot(seu,features = c("CD56","AQP4")) # eerror in plot
DotPlot(seu, features = c("CD56","AQP4","CD24","GLAST","CD140a","CD29","CD184","CD71","O4","HepaCAM","CD133"))
# scale the data
seu <- ScaleData(seu)
DotPlot(seu, features = c("CD56","AQP4","CD24","GLAST","CD140a","CD29","CD184","CD71","O4","HepaCAM","CD133"))
DotPlot(seu, features = allAB)
#do i need to normalize?
# seu <- NormalizeData(seu, normalization.method = "LogNormalize", scale.factor = 10000)
# create a UMAP
# to do so PCA must be run first
seu <- FindVariableFeatures(seu) # needed for PCA or select all features
#dimentionality reduction methods: pca and umap
seu <-RunPCA(seu,seed.use = 42) # will use variable features by default
seu <- RunPCA(seu, features = allAB)
print(seu[["pca"]], dims = 1:5, nfeatures = 5) # tells you what AB are contributing to the
seu <- RunUMAP(seu,features = allAB)
# Add the clustering data to the seurat object
seu <- AddMetaData(object=seu, metadata=metaClustering[fs$map$mapping[,1]], col.name = 'flowSOM.k.8')
DimPlot(seu, group.by = "flowSOM", reduction = "pca")
DimPlot(seu, group.by = "flowSOM", reduction = "umap") # from normal PCA used as umap input it is mostly on blob, I get the same shape when using all AB features as PCA input
DotPlot (seu, features = allAB, group.by = "flowSOM")
#allow for color labeling
Idents(seu) <- "flowSOM"
FLowSOM clustering visually appear to be terrible.
I’ll just try the seurat clustering
# cluster using Louvain clustering
# using the square root of the number of cells for K might work better
#sqrt(73578) = 271.25
seu <- FindNeighbors(seu, dims = 1:10, k = 271)
seu <- FindClusters(seu, resolution = c(0.1,0.25,0.5,0.75,1))
clustree(seu, prefix = "RNA_snn_res.") + theme(legend.position = "bottom")
# this is very slow - do not run again in notebook
library(clustree)
clustree(seu, prefix = "RNA_snn_res.") + theme(legend.position = "bottom")

# from Clustree 0.5 looks like the best resolution
# c(0.1,0.25,0.5,0.75,1)
resolutions = c("RNA_snn_res.0.1","RNA_snn_res.0.25","RNA_snn_res.0.5","RNA_snn_res.0.75","RNA_snn_res.1")
for (res in resolutions){
print(DimPlot(seu, reduction = "umap", repel = TRUE, label = TRUE, group.by = res))
}





NA
NA
The clusters are not well separated, but better than with flowSOM (visually) Lets see how a dotplot looks
DotPlot(seu, group.by = "RNA_snn_res.0.5", features = allAB, scale = TRUE) # why is the intensity ploted?
# the data must not be working correctly
# maybe the object is not really correct at all
FeatureScatter(seu, feature1 = "CD56",feature2 = "CD24")
FeatureScatter(seu, feature1 = "CD29",feature2 = "CD184")
# maybe the data needs to be normalized
seu <- NormalizeData(seu)
DoHeatmap(seu, features = allAB, group.by = "RNA_snn_res.0.5")
# the groups look okay by heatmap. I'll try the different resolutions
# c(0.1,0.25,0.5,0.75,1)
resolutions = c("RNA_snn_res.0.1","RNA_snn_res.0.25","RNA_snn_res.0.5","RNA_snn_res.0.75","RNA_snn_res.1")
for (res in resolutions){
print(DoHeatmap(seu, features = allAB, group.by = res))
}





# try the dotplot again
# c(0.1,0.25,0.5,0.75,1)
resolutions = c("RNA_snn_res.0.1","RNA_snn_res.0.25","RNA_snn_res.0.5","RNA_snn_res.0.75","RNA_snn_res.1")
for (res in resolutions){
print(DotPlot(seu, features = allAB, group.by = res, cols = c("blue","red")))
}
# what is happening?
Try feature maps
for (AB in allAB){
print(FeaturePlot(seu, features = AB),slot = 'scale.data',min.cutoff = 'q10', max.cutoff ='90')
}













# autothreshold isn't great - I set quartiles
#FeaturePlot(seu, features = "O4", min.cutoff = 1, max.cutoff = 25)
#FeaturePlot(seu, features = "O4", slot = 'scale.data',min.cutoff = 0.1, max.cutoff = 25)
#FeaturePlot(seu, features = "O4", slot = 'scale.data',min.cutoff = 'q5', max.cutoff ='99')
Try ridge plots
RidgePlot(seu, features = c("CD56","CD24"), ncol = 2) # missing values???
VlnPlot(seu, features = c("CD56","CD24"), ncol = 2) # also missing values???
I’ll save the seurat object and see about trying to cluster with phenograph. I believe I have read in the unalligned not normalized data
saveRDS(seu, "/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/9MBO/prepro_outsjan20-9000cells/SeuratfromFlowsom.rds")
Phenograph clustering uses Jaccard coefficient between neruest neigbors sets and then id communities by louvain
# install
if(!require(devtools)){
install.packages("devtools") # If not already installed
}
devtools::install_github("JinmiaoChenLab/Rphenograph")
library(Rphenograph)
LS0tCnRpdGxlOiAic3RhdHM6IHNpbGhvdWV0dGUgc2NvcmUsIGNoLCBkYmkiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KClBhcnQgMTogc3RhdHMKYGBge3J9CgojaW5zdGFsbCByZXF1aXJlZCBwYWNrYWdlCmlmICghcmVxdWlyZSgiQmlvY01hbmFnZXIiLCBxdWlldGx5ID0gVFJVRSkpCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpCgpCaW9jTWFuYWdlcjo6aW5zdGFsbCgiRmxvd1NPTSIpICNmb3IgZmxvd3NvbSBjbHVzdGVyaW5nCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJmbG93Q29yZSIpICNmb3IgZmxvd3NvbSBjbHVzdGVyaW5nCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJjbHVzdGVyIikgI2ZvciBzaWxob3VldHRlIHNjb3JlCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJmcGMiKSAjZm9yIGNoIGluZGV4CkJpb2NNYW5hZ2VyOjppbnN0YWxsKCdjbHYnKSAjZm9yIGRiaQpCaW9jTWFuYWdlcjo6aW5zdGFsbCgncGFyRGlzdCcpICMgZm9yIG1ha2luZyBkaXN0cmFuY2UgbWF0cml4IGlucHV0IGZvciBzaWxvdWV0dGUKCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCdTZXVyYXQnKSAjZm9yIHBjYSBhbmQgZmxvd3NvbSB2aXN1YWxpemF0aW9uCgppbnN0YWxsLnBhY2thZ2VzKCJwYXJhbGxlbERpc3QiKSAjIGRvZXNuJ3QgaW1wcm92ZSBtZW1vcnkgb24gbXkgbWFjIG1heWJlIG9uIGdydW1pbwppbnN0YWxsLnBhY2thZ2VzKCJ3b3Jkc3BhY2UiKSAjIGNvbnRhaW5zIGRpc3QubWF0cml4IGZ1bmN0aW9uCgpgYGAKCmBgYHtyfQpsaWJyYXJ5KEZsb3dTT00pCmxpYnJhcnkoZmxvd0NvcmUpCmxpYnJhcnkoY2x1c3RlcikKbGlicmFyeShmcGMpCmxpYnJhcnkoY2x2KQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShkcGx5cikKCmxpYnJhcnkoInBhcmFsbGVsRGlzdCIpCmxpYnJhcnkoIndvcmRzcGFjZSIpCgpybShsaXN0PWxzKCkpCgpgYGAKCgpgYGB7cn0KCiNpbnB1dCBmaWxlIHBhdGgsIGNoYW5nZSBpZiBuZWVkZWQKZmlsZU5hbWUgPC0iL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvRmxvd0N5dG9tZXRyeS9QaGVub0lEL0FuYWx5c2lzLzlNQk8vcHJlcHJvX291dHNqYW4yMC05MDAwY2VsbHMvcHJlcHJvX291dHNmbG93c2V0LmNzdiIKCiMgbm90ZTogY3VycmVudCBtYXRyaXggc2FtcGxlIElEIGhhdmUgY2VsbCBpbmRleCAjIGF0dGFjaGVkLiAKCmRmIDwtIHJlYWQuY3N2KGZpbGVOYW1lKQpoZWFkKGRmKQpwcmludChkaW0oZGYpKSAjIHRoaXMgaXMgc3BlY2lmaWMgZGYgaGFzIDczNTc4IGNlbGxzCiMgdGhlIHByZXByb2Nlc3Npbmcgb3V0cHV0IGNzdiBuZWVkcyB0byBiZSBjbGVhbmVkIC0gaXQgY29udGFpbnMgbGl2ZSBkZWFkLCBGU0MsIFNTQyBhbmQgdGhlIHNhbXBsZSBjb2x1bW4KZGYyIDwtIGRmICU+JSBzZWxlY3QoLWMoIkxpdmUuRGVhZCIsRlNDLFNTQyxYLEJhdGNoLGNlbGwpKQoKbSA8LSBhcy5tYXRyaXgoZGYyKSAjIGZsb3dzZXQgdGFrZXMgaW5hIG1hdHJpeCBub3QgZGYgCiNtIDwtIG9tWzE6MzAwMCxdICNzdWJzZXQgKG49MzAwMCksIG9taXQgdGhpcyB0byB0ZXN0IHRoZSB3aG9sZSBmaWxlICAtIEkgZGlkbid0IHN1YnNldCBoZXJlIGJ1dCB0b28gOTAwMCBvciBtYXggY2VsbHMgZnJvbSBlYWNoIGZpbGUgYmVmb3JlCgojIGNyZWF0ZSBkaXN0YW5jZSBtYXRyaXgKZG0gPC0gZGlzdC5tYXRyaXgobSwgYXMuZGlzdCA9IFRSVUUpCgojIGlkZWFzIHRvIHNvbHZlIGRpc3QgbWF0cml4IHByb2JsZW06CiMgMSBjYWxjdWxhdGUgaW4gcHl0aG9uIGFuZCBzYXZlIGNzdiB0aGVuIHJlYWQgYXMuZGlzdAojIDIgYWZ0ZXIgY2x1c3RlcmluZyBnZXQgb3V0IHRoZSBleHByZXNzaW9uIG1hdHJpeCBhbmQgdGhlIGNsdXN0ZXIgaWRlYXMgbWFrZSBhIGRmIHN1YnNldCByb3dzICgyNTAwKSwgc2VwYXJhdGUgaW50byBleHByZXNzaW9uIG1hdHJpeCBhbmQgY2x1c3RlciBJRCB2ZWN0b3JzLCBjYWxjdWxhdGUgZGlzdCBtYXRyaXggYW5kIGlucHV0IGludG8gdGhlIHNpbG91ZXR0ZSBmdW5jdGlvbi4gCgojdHJ5IHNjYWxpbmcKIyBTT00gPSBzZWxmIG9yZ2FuaXppbmcgbWFwLCBNU1QgPSBtaW5pbWFsIHNwYW5uaW5nIHRyZWUKCiMgaWYgcmVhZGluZyBpbiBhIGNzdiBjb252ZXJ0IHRvIGZsb3dzZXQKZnJhbWUgPC0gbmV3KCJmbG93RnJhbWUiLCBleHBycyA9IG0pICNjb252ZXJ0IGlucHV0IHRvIGZsb3dmcmFtZQpmcyA8LSBSZWFkSW5wdXQoZnJhbWUpICNjb252ZXJ0IGZsb3dmcmFtZSB0byBmbG93c29tIG9iamVjdAojIGZzIDwtIEJ1aWxkU09NKGZzLGNvbHNUb1VzZT0oLTEpKSAjLTEgYmVjYXVzZSB3ZSBhcmUgbm90IHVzaW5nICJYIiBjb2x1bW4gdG8gYnVpbGQgU09NCmZzIDwtIEJ1aWxkU09NKGZzKSAjIGJ1aWxkIGZsb3dTT00gb2JqZWN0LCBubyBuZWVkIGZvciAtMSBiZWNhdXNlIEkgY2xlYW5lZCB0aGUgZGYgYWJvdXQgYmVmb3JlIG1ha2luZyBmbG93c2V0IApmcyA8LSBCdWlsZE1TVChmcykgIyBidWlsZCBtaW5pbXVtIHNwYW5uaW5nIHRyZWUgCiMgQnVpbGRNU1QoZmxvd1NPTSBvYmplY3QgZ2VuZXJhdGVkIGJ5IGJ1aWxkU09NKQoKIyBzYXZlIHRoZSBGbG93U09NIE1TVCBvYmplY3QgdG8gdHJ5IGFuZCBhdm9pZCBtZW1vcnkgZXJyb3IKI3NhdmVSRFMoZnMsIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL0Zsb3dDeXRvbWV0cnkvUGhlbm9JRC9BbmFseXNpcy85TUJPL3ByZXByb19vdXRzamFuMjAtOTAwMGNlbGxzL0Zsb3dTT01pbi5yZHMiKQoKCmBgYAoKU2F2ZWQgdGhlIGZsb3dzZXQgb2JqZWN0IEkgY3JlYXRlZCBhbmQgbG9hZCB0aGUgb2JqZWN0IGhlcmUgLSBidXQgSSBkaWRuJ3Qgc2F2ZSB0aGUgZmlsdGVyZWQgZGYgY29udmVydGVkIHRvIG1hdHJpeCwgd2hpY2ggaXMgbmVlZGVkIGZvciB0aGUgc3RhdHMgYmVsb3cKCmBgYHtyfQpmcyA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9GbG93Q3l0b21ldHJ5L1BoZW5vSUQvQW5hbHlzaXMvOU1CTy9wcmVwcm9fb3V0c2phbjIwLTkwMDBjZWxscy9GbG93U09NaW4ucmRzIikKCiMgbm90ZSB0byBTaHVtaW5nIC0gd2Ugc2hvdWxkIGJlIGFibGUgdG8gZ28gZGlyZWN0bHkgZnJvbSBmbG93c2V0IGluIHByZXByb2Nlc3NpbmcgaW50byB0aGlzIGZsb3dzZXQgYnV0IHRoZW4gdGhlIDkgc2FtcGxlcyAob3IgaG93ZXZlciBtYW55IHdpbGwgYmUgaW4gc2VwYXJhdGUgZnJhbWVzKQojIEkgZG9uJ3Qga25vdyB3aGVyZSB0aGUgYWN0dWFsIHZhbHVlcyBhcmUgbG9jYXRlZAoKYGBgCgoKCmBgYHtyfQojIHNraXAKI2Jhc2VkIG9uIGZsb3dzb20sIHRoZSBvcHRpbWFsIGsgbnVtYmVyIG9mIGNsdXN0ZXJzID0gNSBmb3IgdGhlIHNhbXBsZSAobj0zMDAwKSAKbWV0YWNsIDwtIE1ldGFDbHVzdGVyaW5nKGZzJG1hcCRjb2RlcywKIm1ldGFDbHVzdGVyaW5nX2NvbnNlbnN1cyIsCm1heCA9IDE1KQoKdW5pcXVlKG1ldGFjbFtmcyRtYXAkbWFwcGluZ1ssIDFdXSkKYGBgCgoKCgoKYGBge3J9CgojYSBmdW5jdGlvbiB0aGF0IGNhbGN1bGF0ZSAzIHN0YXRzIGZvciBrIG51bWJlciBvZiBjbHVzdGVyaW5nCiMgdGhlIHNpbGhvdWV0dGUgc3RhdCB0YWtlcyB0b28gbXVjaCBtZW1vcnkgYW5kIHdlIHdpbGwgbm90IHJ1biB0aGlzIGZvciBub3cuCgojIFNodW1pbmcgSSBuZWVkIHRoaXMgZnVuY3Rpb24gdG8gdGFrZSBpbiBhIGxpc3Qgb2YgY2x1c3RlcmluZyBpbmRleGVzIGFuZCBhbiBleHByZXNzaW9uIG1hdHJpeAoKc3RhdHMgPC0gZnVuY3Rpb24oa3JhbmdlKXsKICMgc2lfbGkgPC1saXN0KCkKICBjaF9saSA8LWxpc3QoKQogIGRiaV9saSA8LWxpc3QoKQogIAogIGsgPSAzCiAgZG0gPSBwYXJEaXN0KG0pCiAgZG0gPSBkaXN0KG0sIG1ldGhvZCA9ICJtaW5rb3dza2kiKQogIGRtID0gZGlzdC5tYXRyaXgobSwgYXMuZGlzdCA9IFRSVUUpCiAgCiAgZm9yIChrIGluIGtyYW5nZSkgewogICAgCiAgICAjZmxvd3NvbSBjbHVzdGVyaW5nLCB0cnkgZWFjaCBrIGluIGtyYW5nZQogICAgbWV0YUNsdXN0ZXJpbmcgPC0gKG1ldGFDbHVzdGVyaW5nX2NvbnNlbnN1cyhmcyRtYXAkY29kZXMsayA9IGssc2VlZD00MikpICMgaGVyZSB3ZSBhcmUgY2x1c3RlcmluZyB3aXRoaW4gdGhlIGxvb3AKICAgICMgQFNodW1pbmcgd2UgbmVlZCB0byBjaGFuZ2UgdGhpcyBzbyB0aGF0IHRoZSBsb29wIGNhbiBwdWxsIHRoZSByZXN1bHRzIG9mIGNsdXN0ZXJpbmcgb3V0IG9mIGFub3RoZXIgZGF0YSBvYmplY3QKICAgICMgaG93IGFtIEkgZ29pbmcgdG8gdGVzdCB0aGUgU2V1cmF0IG9yIFBoZW5vZ3JhcGggY2x1c3RlcmluZz8KCiAgICAjY2FsY3VsYXRlIHNpbGhvdWV0dGUgc2NvcmUKICAgICMgY2x1c3RlciBpbmRleCwgZGlzdGFuY2UgbWF0cml4IG9mIHBhaXJzIC0tLSAgbWF5YmUgY2FsY3VsYXRlIGluIGFkdmFuY2UgCiAgICBzaSA8LSBzaWxob3VldHRlKG1ldGFDbHVzdGVyaW5nW2ZzJG1hcCRtYXBwaW5nWywgMV1dLGRpc3QobSksKQoKICAgIHNpX2xpW2tdIDwtIG1lYW4oc2lbLCAzXSkKICAgIAogICAgI2NhbGN1bGF0ZSBDYWxpbnNraS1IYXJhYmFzeiBpbmRleAogICAgIyBjYWxpbmhhcmEoeCxjbHVzdGVyaW5nLGNuPW1heChjbHVzdGVyaW5nKSkKICAgICMgd2hlcmUgeCBpcyBhIG1hdHJpeCBvciBkYXRhZnJhbWUKICAgIGNoID0gY2FsaW5oYXJhKG0sbWV0YUNsdXN0ZXJpbmdbZnMkbWFwJG1hcHBpbmdbLCAxXV0sY249bWF4KG1ldGFDbHVzdGVyaW5nW2ZzJG1hcCRtYXBwaW5nWywxXV0pKQogICAgCiAgICBjaF9saVtrXSA8LSBjaAogICAgCiAgICAjY2FsY3VsYXRlIERhdmllc+KAk0JvdWxkaW4gaW5kZXgKICAgIGRiaSA9IGNsdi5EYXZpZXMuQm91bGRpbihjbHMuc2NhdHQuZGF0YSgKICAgICAgbSwKICAgICAgbWV0YUNsdXN0ZXJpbmdbZnMkbWFwJG1hcHBpbmdbLCAxXV0pLAogICAgICBpbnRyYWNscyA9ICJhdmVyYWdlIiwKICAgICAgaW50ZXJjbHMgPSAiYXZlcmFnZSIKICAgICkKCiAgICBkYmlfbGlba10gPC0gZGJpWzFdCiAgICB9IAoKICByZXR1cm4obGlzdChjaF9saSwgZGJpX2xpKSkKfQoKCgpgYGAKCgpgYGB7cn0KIyBydW4gdGhlIGZ1bmN0aW9uIHRvIGdldCB0aGUgc3RhdHMKCgoKa3JhbmdlIDwtIDM6MTUgI3JhbmdlIG9mIG51bWJlciBvZiBjbHVzdGVycwpsaSA9IHN0YXRzKGtyYW5nZSkgICMgZG9lc24ndCBjb250YWluIHNpbGhvdWV0dGUgCiMgbGlzdCBvZiAyIAoKI3NpbGhvdWV0dGUgc2NvcmU6IHJhbmdlcyBmcm9tIC0xICB0byAxIAojLTE6IGJhZCBjbHVzdGVycyAgMDogbmV1dHJhbCwgaW5kaWZmZXJlbnQgIDE6IGdvb2QgY2x1c3RlcnMKI3Bsb3Qoa3JhbmdlLCB0eXBlPSdiJywgbGlbWzFdXVtrcmFuZ2VdLCB4bGFiPSdOdW1iZXIgb2YgY2x1c3RlcnMnLCB5bGFiPSdBdmVyYWdlIFNpbGhvdWV0dGUgU2NvcmVzJywgZnJhbWU9VFJVRSkKCiNDYWxpbnNraS1IYXJhYmFzeiBpbmRleDogCiMgdGhlIGhpZ2hlc3QgdmFsdWUgaXMgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzCnBsb3Qoa3JhbmdlLCB0eXBlPSdiJywgbGlbWzFdXVtrcmFuZ2VdLCB4bGFiPSdOdW1iZXIgb2YgY2x1c3RlcnMnLCB5bGFiPSdDYWxpbnNraS1IYXJhYmFzeiBpbmRleCcsIGZyYW1lPVRSVUUpCgojRGF2aWVz4oCTQm91bGRpbiBpbmRleDogbWluaW11bSBzY29yZSBpcyB6ZXJvCiN0aGUgbG93ZXN0IHZhbHVlIGlzIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycwpwbG90KGtyYW5nZSwgdHlwZT0nYicsIGxpW1syXV1ba3JhbmdlXSwgeGxhYj0nTnVtYmVyIG9mIGNsdXN0ZXJzJywgeWxhYj0nRGF2aWVz4oCTQm91bGRpbiBpbmRleCcsIGZyYW1lPVRSVUUpCgoKYGBgClRoZSBDYWxpbnNraS1IYXJhYmFzeiBpbmRleCBzaG93IHRoYXQgSz04IGlzIGJlc3QgbnVtYmVyIG9mIGNsdXN0ZXJzLiAgQnV0IHRoZSBEYXZpZXPigJNCb3VsZGluIGluZGV4IHNob3dzIGxvd2VyIGlzIGJldHRlci4gIEl0IGlzIG9rYXkgdXAgdW50aWwgOS4gCgoKVHJ5IHN0YXRpc3RpY3Mgb24gb3RoZXIgY2x1c3RlcmluZy4gIAoKCmBgYHtyfQojIGZyb20gUGhlbm9ncmFwaCAKIyByZWFkIGluIHRoZSBkZiB3aXRoIHBoZW5vZ3JhcGggY2x1c3RlcnMgYW5kIHNhdmVkIGV4cHJlc3Npb24gbWF0cml4IG91dHB1dCBmcm9tIHByZXByb2Nlc3NpbmcgLSBubyBhbGlnbmVtZW50IG9yIG5vcm1hbGl6YXRpb24KcHJpbnQoY29sbmFtZXMoZGYpKQphbGxBQiA8LSBjKCJBUVA0IiwiQ0Q1NiIsIkdMQVNUIiwgIkNEMTQwYSIsIkNEMjkiLCAiQ0Q0NCIsICJDRDE4NCIsIkNENzEiLCJDRDI0IiwiQ0QxNSIsIk80IiwiSGVwYUNBTSIsIkNEMTMzIikKIyB3ZSBuZWVkIGp1c3QgdGhlIGV4cHJlc3Npb24gbWF0cml4IApkZjIgPC0gZGYgJT4lIHNlbGVjdChjKCJBUVA0IiwiQ0Q1NiIsIkdMQVNUIiwgIkNEMTQwYSIsIkNEMjkiLCAiQ0Q0NCIsICJDRDE4NCIsIkNENzEiLCJDRDI0IiwiQ0QxNSIsIk80IiwiSGVwYUNBTSIsIkNEMTMzIikpCgptIDwtIGFzLm1hdHJpeChkZjIpCgoKI2NhbGN1bGF0ZSBDYWxpbnNraS1IYXJhYmFzeiBpbmRleAogICAgIyBjYWxpbmhhcmEoeCxjbHVzdGVyaW5nLGNuPW1heChjbHVzdGVyaW5nKSkKICAgICMgd2hlcmUgeCBpcyBhIG1hdHJpeCBvciBkYXRhZnJhbWUKY2gucGhlbm8uay4yNzEgPSBjYWxpbmhhcmEobSxkZiRwaGVub2dyYXBoX2NsdXN0ZXJrMjcxLGxlbmd0aCgodW5pcXVlKGRmJHBoZW5vZ3JhcGhfY2x1c3RlcmsyNzEpKSkpCgpjaC5waGVuby5rLjUwID0gY2FsaW5oYXJhKG0sZGYkcGhlbm9ncmFwaF9jbHVzdGVyLGxlbmd0aCgodW5pcXVlKGRmJHBoZW5vZ3JhcGhfY2x1c3RlcikpKSkgICAKCiMgdGhpcyB3b3JrcyB3ZWxsIHRvIHNlZSB0aGUgc3RhdGlzdGljcyBidXQgbm90IGdyZWF0IGZvciBhIHBsb3QuCiMgSSBtaWdodCBuZWVkIHRvIGp1c3QgbWFrZSBhIHRhYmVsCgoKCgpgYGAKCkRhdmlkIEJvdWxkaW4gaW5kZXggZm9yIFBoZW5vZ3JhcGggCgpgYGB7cn0KI2NhbGN1bGF0ZSBEYXZpZXPigJNCb3VsZGluIGluZGV4CiMgaW5kaWV4Lmxpc3QsIGludHJhY2xzLCBpbnRlcmNscyAKIyBJJ20gbm90IHN1cmUgaG93IHRoaXMgaXMgd29ya2luZyBidXQgaXQgZG9lcyBydW4KZGJpLnBoZW4uay4yNzEgPC0gY2x2LkRhdmllcy5Cb3VsZGluKGNscy5zY2F0dC5kYXRhKAogICAgICBtLAogICAgICBkZiRwaGVub2dyYXBoX2NsdXN0ZXJrMjcxKSwKICAgICAgaW50cmFjbHMgPSAiYXZlcmFnZSIsCiAgICAgIGludGVyY2xzID0gImF2ZXJhZ2UiCiAgICApCiAgCmRiaS5waGVuLmsuNTAgPC0gY2x2LkRhdmllcy5Cb3VsZGluKGNscy5zY2F0dC5kYXRhKAogICAgICBtLAogICAgICBkZiRwaGVub2dyYXBoX2NsdXN0ZXIpLAogICAgICBpbnRyYWNscyA9ICJhdmVyYWdlIiwKICAgICAgaW50ZXJjbHMgPSAiYXZlcmFnZSIKICAgICkKCgpgYGAKCgptYWtlIHRoaXMgY2FsY3VsYXRpb25zIG9uIHRoZSBTZXVyYXQgY2x1c3RlcmluZwoKCmBgYHtyfQojIHJlYWQgaW4gdGhlIHNldXJhdCBvYmplY3Qgd2l0aCB0aGUgY2x1c3RlcmluZyB2YWx1ZXMKc2V1IDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL0Zsb3dDeXRvbWV0cnkvUGhlbm9JRC9BbmFseXNpcy85TUJPL3ByZXByb19vdXRzamFuMjAtOTAwMGNlbGxzL1NldXJhdGZyb21GbG93c29tUGhlbm8ucmRzIiApCgoKYGBgCgpDSCBpbmRleCBhbmQgREJJIGZyb20gU2V1cmF0IChMb3V2YWluKSB0cnkgb25lIHJlc29sdXRpb24gZmlyc3QKCmBgYHtyfQoKY2gubG91di5yZXMuMSA9IGNhbGluaGFyYShtLHNldUBtZXRhLmRhdGEkUk5BX3Nubl9yZXMuMSxsZW5ndGgoKHVuaXF1ZShzZXVAbWV0YS5kYXRhJFJOQV9zbm5fcmVzLjEpKSkpCiMgdGhpcyB3b3JrcwoKZGJpLmxvdXYucmVzLjEgPC0gY2x2LkRhdmllcy5Cb3VsZGluKGNscy5zY2F0dC5kYXRhKAogICAgICBtLAogICAgICBzZXVAbWV0YS5kYXRhJFJOQV9zbm5fcmVzLjEpLAogICAgICBpbnRyYWNscyA9ICJhdmVyYWdlIiwKICAgICAgaW50ZXJjbHMgPSAiYXZlcmFnZSIKICAgICkKIyB0aGlzIGRvZXNuJ3Qgd29yayAtIG5vdCBzdXJlIHdoZXJlIHRvIHNwZWNpZnkgaG93IHRvIGZpbmQgdGhlIGRhdGEgdG8gY2FsY3VsYXRlIGludHJhY2xzIGFuZCBpbnRlcmNscwoKCmBgYAoKQ2FsY3VsYXRlIGFsbCBDSEkgZm9yIExvdXZhaW4KCmBgYHtyfQoKICBjaF9sb3V2IDwtbGlzdCgpCmwucmFuZ2UgPC0gYygiUk5BX3Nubl9yZXMuMC4xIiwiUk5BX3Nubl9yZXMuMC4yNSIsIlJOQV9zbm5fcmVzLjAuNSIsIlJOQV9zbm5fcmVzLjAuNzUiLCJSTkFfc25uX3Jlcy4xIikKcmVzb2x1dGlvbiA9IGMoMC4xLDAuMjUsMC41LDAuNzUsMSkKcHJlZml4ID0gIlJOQV9zbm5fcmVzLiIgCiMgbG9vcCBkb2Vzbid0IHdvcmsgYmVjYXVzZSBJIGNhbid0IGluZGV4IHRoZSBzZXVyYXQgcmVzb2x1dGlvbnMKCmNoID0gY2FsaW5oYXJhKG0sc2V1QG1ldGEuZGF0YSRSTkFfc25uX3Jlcy4wLjEsbGVuZ3RoKCh1bmlxdWUoc2V1QG1ldGEuZGF0YSRSTkFfc25uX3Jlcy4wLjEpKSkpCmNoX2xvdXZbIlJOQV9zbm5fcmVzLjAuMSJdIDwtIGNoCgpjaCA9IGNhbGluaGFyYShtLHNldUBtZXRhLmRhdGEkUk5BX3Nubl9yZXMuMC4yNSxsZW5ndGgoKHVuaXF1ZShzZXVAbWV0YS5kYXRhJFJOQV9zbm5fcmVzLjAuMjUpKSkpCmNoX2xvdXZbIlJOQV9zbm5fcmVzLjAuMjUiXSA8LSBjaAoKY2ggPSBjYWxpbmhhcmEobSxzZXVAbWV0YS5kYXRhJFJOQV9zbm5fcmVzLjAuNSxsZW5ndGgoKHVuaXF1ZShzZXVAbWV0YS5kYXRhJFJOQV9zbm5fcmVzLjAuNSkpKSkKY2hfbG91dlsiUk5BX3Nubl9yZXMuMC41Il0gPC0gY2gKCmNoID0gY2FsaW5oYXJhKG0sc2V1QG1ldGEuZGF0YSRSTkFfc25uX3Jlcy4wLjc1LGxlbmd0aCgodW5pcXVlKHNldUBtZXRhLmRhdGEkUk5BX3Nubl9yZXMuMC43NSkpKSkKY2hfbG91dlsiUk5BX3Nubl9yZXMuMC43NSJdIDwtIGNoCgpjaCA9IGNhbGluaGFyYShtLHNldUBtZXRhLmRhdGEkUk5BX3Nubl9yZXMuMSxsZW5ndGgoKHVuaXF1ZShzZXVAbWV0YS5kYXRhJFJOQV9zbm5fcmVzLjEpKSkpCmNoX2xvdXZbIlJOQV9zbm5fcmVzLjEiXSA8LSBjaAoKCgoKYGBgCgpQbG90IHRoZSBDSEkgZnJvbSBMb3V2YWluIGNsdXN0ZXJzCmBgYHtyfQoKIyB0aGUgaGlnaGVzdCB2YWx1ZSBpcyB0aGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMKeSA8LSBzYXBwbHkoY2hfbG91diwgJ1snLCApCnBsb3QocmVzb2x1dGlvbiwgeSwgeGxhYj0nUmVzb2x1dGlvbiBvZiBjbHVzdGVycycsdHlwZSA9ICJiIiwgeWxhYj0nQ2FsaW5za2ktSGFyYWJhc3ogaW5kZXgnLCBmcmFtZT1UUlVFKQoKCgoKYGBgCgpNYWtlIGEgdGFibGUgd2l0aCB0aGUgQ0hJIG91dHB1dHMKCmBgYHtyfQoKQ2x1c3Rlci50eXBlIDwtIGMoIlBoZW5vZ3JhcGguay41MCIsIlBoZW5vZ3JhcGguay4yNzEiLGwucmFuZ2UpCnByaW50KENsdXN0ZXIudHlwZSkKTWV0aG9kIDwtIGMoIlBoZW5vZ3JhcGguay41MCIgLCAiUGhlbm9ncmFwaC5rLjI3MSIgLCJSTkFfc25uX3Jlcy4wLjEiLCAgIlJOQV9zbm5fcmVzLjAuMjUiICwiUk5BX3Nubl9yZXMuMC41IiAsICJSTkFfc25uX3Jlcy4wLjc1IiwgIlJOQV9zbm5fcmVzLjEiKQoKcHJpbnQoeSkKCkNISS52YWx1ZSA8LSBjKDc0MTguMjk5LDc4MDYuMTMwLCA1MjUyMS4xMjAsICA5OTMwLjIzNSwgIDc3MzQuMDY1LCAgNDk0OS41OTMsICA0MjI5LjI1MikKCgoKZGYuY2hpIDwtIGNiaW5kLmRhdGEuZnJhbWUoTWV0aG9kLCBDSEkudmFsdWUpCgoKCgpgYGAKCk1ha2UgYW5vdGhlciBwbG90CgpgYGB7cn0KI2xpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGRmLmNoaSwgYWVzKHg9IE1ldGhvZCwgeT0gQ0hJLnZhbHVlKSkgKyBnZW9tX3BvaW50KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQoKIyBob3cgZG8gdGhlc2UgdmFsdWVzIGNvbXBhcmUgd2l0aCAKCmBgYAoKCgoKUGFydCAyOiBwbG90CgpgYGB7cn0KCiN0cmFuc3Bvc2UgdGhlIGNzdiBzbyB0aGF0IHNldXJhdCBvYmplY3QgaGFzIHRoZSByaWdodCBjb2x1bW4gYW5kIHJvdwojIHRoZSBmbG93IGludGVuc2l0eSB2YWx1ZXMgd2lsbCBiZSBpbnB1dCBhcyBSTkEgZXhwcmVzc2lvbgp0bSA8LSB0KGRmMikKcm93bmFtZXModG0pIDwtIGNvbG5hbWVzKGRmMikKY29sbmFtZXModG0pIDwtIHJvd25hbWVzKGRmMikKCgprIDwtIDEwICNjaGFuZ2UgbnVtYmVyIG9mIGNsdXN0ZXIgaGVyZSAKayA8LSA4ICMgYmVzdCBudW1iZXIgb2YgY2x1c3RlcnMgYWNjb3JkaW5nIHRvIENIIGluZGV4CgptZXRhQ2x1c3RlcmluZyA8LSAobWV0YUNsdXN0ZXJpbmdfY29uc2Vuc3VzKGZzJG1hcCRjb2RlcyxrID0gayxzZWVkPTQyKSkgIyBmbG93U09NIGNsdXN0ZXJpbmc/CgpzZXUgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KHRtKSAjIGNyZWF0ZSBhIHNldXJhdCBvYmplY3QgCiMgY2hlY2sgdGhlIHNldXJhdCBvYmplY3QgYnkgcGxvdHRpbmcgc29tZSBmZWF0dXJlcwpwcmludChjb2xuYW1lcyhkZjIpKQphbGxBQiA8LSBjb2xuYW1lcyhkZjIpClZsblBsb3Qoc2V1LGZlYXR1cmVzID0gYygiQ0Q1NiIsIkFRUDQiKSkgIyBlZXJyb3IgaW4gcGxvdApEb3RQbG90KHNldSwgZmVhdHVyZXMgPSBjKCJDRDU2IiwiQVFQNCIsIkNEMjQiLCJHTEFTVCIsIkNEMTQwYSIsIkNEMjkiLCJDRDE4NCIsIkNENzEiLCJPNCIsIkhlcGFDQU0iLCJDRDEzMyIpKQojIHNjYWxlIHRoZSBkYXRhIApzZXUgPC0gU2NhbGVEYXRhKHNldSkKCkRvdFBsb3Qoc2V1LCBmZWF0dXJlcyA9IGMoIkNENTYiLCJBUVA0IiwiQ0QyNCIsIkdMQVNUIiwiQ0QxNDBhIiwiQ0QyOSIsIkNEMTg0IiwiQ0Q3MSIsIk80IiwiSGVwYUNBTSIsIkNEMTMzIikpCkRvdFBsb3Qoc2V1LCBmZWF0dXJlcyA9IGFsbEFCKQojZG8gaSBuZWVkIHRvIG5vcm1hbGl6ZT8KIyBzZXUgPC0gTm9ybWFsaXplRGF0YShzZXUsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIsIHNjYWxlLmZhY3RvciA9IDEwMDAwKQoKIyBjcmVhdGUgYSBVTUFQCiMgdG8gZG8gc28gUENBIG11c3QgYmUgcnVuIGZpcnN0IApzZXUgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoc2V1KSAjIG5lZWRlZCBmb3IgUENBIG9yIHNlbGVjdCBhbGwgZmVhdHVyZXMKI2RpbWVudGlvbmFsaXR5IHJlZHVjdGlvbiBtZXRob2RzOiBwY2EgYW5kIHVtYXAKCnNldSA8LVJ1blBDQShzZXUsc2VlZC51c2UgPSA0MikgIyB3aWxsIHVzZSB2YXJpYWJsZSBmZWF0dXJlcyBieSBkZWZhdWx0CnNldSA8LSBSdW5QQ0Eoc2V1LCBmZWF0dXJlcyA9IGFsbEFCKQoKcHJpbnQoc2V1W1sicGNhIl1dLCBkaW1zID0gMTo1LCBuZmVhdHVyZXMgPSA1KSAjIHRlbGxzIHlvdSB3aGF0IEFCIGFyZSBjb250cmlidXRpbmcgdG8gdGhlIApzZXUgPC0gUnVuVU1BUChzZXUsZmVhdHVyZXMgPSBhbGxBQikKCiMgQWRkIHRoZSBjbHVzdGVyaW5nIGRhdGEgdG8gdGhlIHNldXJhdCBvYmplY3QKc2V1IDwtIEFkZE1ldGFEYXRhKG9iamVjdD1zZXUsIG1ldGFkYXRhPW1ldGFDbHVzdGVyaW5nW2ZzJG1hcCRtYXBwaW5nWywxXV0sIGNvbC5uYW1lID0gJ2Zsb3dTT00uay44JykKCkRpbVBsb3Qoc2V1LCBncm91cC5ieSA9ICJmbG93U09NIiwgcmVkdWN0aW9uID0gInBjYSIpIApEaW1QbG90KHNldSwgZ3JvdXAuYnkgPSAiZmxvd1NPTSIsIHJlZHVjdGlvbiA9ICJ1bWFwIikgIyBmcm9tIG5vcm1hbCBQQ0EgdXNlZCBhcyB1bWFwIGlucHV0IGl0IGlzIG1vc3RseSBvbiBibG9iLCBJIGdldCB0aGUgc2FtZSBzaGFwZSB3aGVuIHVzaW5nIGFsbCBBQiBmZWF0dXJlcyBhcyBQQ0EgaW5wdXQKCkRvdFBsb3QgKHNldSwgZmVhdHVyZXMgPSBhbGxBQiwgZ3JvdXAuYnkgPSAiZmxvd1NPTSIpCgoKI2FsbG93IGZvciBjb2xvciBsYWJlbGluZwpJZGVudHMoc2V1KSA8LSAiZmxvd1NPTSIKYGBgCgpGTG93U09NIGNsdXN0ZXJpbmcgdmlzdWFsbHkgYXBwZWFyIHRvIGJlIHRlcnJpYmxlLgoKSSdsbCBqdXN0IHRyeSB0aGUgc2V1cmF0IGNsdXN0ZXJpbmcKCmBgYHtyfQoKIyBjbHVzdGVyIHVzaW5nIExvdXZhaW4gY2x1c3RlcmluZwojIHVzaW5nIHRoZSBzcXVhcmUgcm9vdCBvZiB0aGUgbnVtYmVyIG9mIGNlbGxzIGZvciBLIG1pZ2h0IHdvcmsgYmV0dGVyCiNzcXJ0KDczNTc4KSA9IDI3MS4yNQpzZXUgPC0gRmluZE5laWdoYm9ycyhzZXUsIGRpbXMgPSAxOjEwLCBrID0gMjcxKQpzZXUgPC0gRmluZENsdXN0ZXJzKHNldSwgcmVzb2x1dGlvbiA9IGMoMC4xLDAuMjUsMC41LDAuNzUsMSkpCmNsdXN0cmVlKHNldSwgcHJlZml4ID0gIlJOQV9zbm5fcmVzLiIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCiMgdGhpcyBpcyAgdmVyeSBzbG93IC0gZG8gbm90IHJ1biBhZ2FpbiBpbiBub3RlYm9vawoKYGBgCgpgYGB7cn0KbGlicmFyeShjbHVzdHJlZSkKY2x1c3RyZWUoc2V1LCBwcmVmaXggPSAiUk5BX3Nubl9yZXMuIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKYGBgCgpgYGB7cn0KIyBmcm9tIENsdXN0cmVlIDAuNSBsb29rcyBsaWtlIHRoZSBiZXN0IHJlc29sdXRpb24KCiMgYygwLjEsMC4yNSwwLjUsMC43NSwxKQpyZXNvbHV0aW9ucyA9IGMoIlJOQV9zbm5fcmVzLjAuMSIsIlJOQV9zbm5fcmVzLjAuMjUiLCJSTkFfc25uX3Jlcy4wLjUiLCJSTkFfc25uX3Jlcy4wLjc1IiwiUk5BX3Nubl9yZXMuMSIpCgpmb3IgKHJlcyBpbiByZXNvbHV0aW9ucyl7CiBwcmludChEaW1QbG90KHNldSwgcmVkdWN0aW9uID0gInVtYXAiLCByZXBlbCA9IFRSVUUsIGxhYmVsID0gVFJVRSwgZ3JvdXAuYnkgPSByZXMpKQogCn0KCgpgYGAKClRoZSBjbHVzdGVycyBhcmUgbm90IHdlbGwgc2VwYXJhdGVkLCBidXQgYmV0dGVyIHRoYW4gd2l0aCBmbG93U09NICh2aXN1YWxseSkKTGV0cyBzZWUgaG93IGEgZG90cGxvdCBsb29rcwoKYGBge3J9CkRvdFBsb3Qoc2V1LCBncm91cC5ieSA9ICJSTkFfc25uX3Jlcy4wLjUiLCBmZWF0dXJlcyA9IGFsbEFCLCBzY2FsZSA9IFRSVUUpICMgd2h5IGlzIHRoZSBpbnRlbnNpdHkgcGxvdGVkPwojIHRoZSBkYXRhIG11c3Qgbm90IGJlIHdvcmtpbmcgY29ycmVjdGx5CgoKIyBtYXliZSB0aGUgb2JqZWN0IGlzIG5vdCByZWFsbHkgY29ycmVjdCBhdCBhbGwKRmVhdHVyZVNjYXR0ZXIoc2V1LCBmZWF0dXJlMSA9ICJDRDU2IixmZWF0dXJlMiA9ICJDRDI0IikKRmVhdHVyZVNjYXR0ZXIoc2V1LCBmZWF0dXJlMSA9ICJDRDI5IixmZWF0dXJlMiA9ICJDRDE4NCIpCgojIG1heWJlIHRoZSBkYXRhIG5lZWRzIHRvIGJlIG5vcm1hbGl6ZWQKc2V1IDwtIE5vcm1hbGl6ZURhdGEoc2V1KQoKRG9IZWF0bWFwKHNldSwgZmVhdHVyZXMgPSBhbGxBQiwgZ3JvdXAuYnkgPSAiUk5BX3Nubl9yZXMuMC41IikKIyB0aGUgZ3JvdXBzIGxvb2sgb2theSBieSBoZWF0bWFwLiBJJ2xsIHRyeSB0aGUgZGlmZmVyZW50IHJlc29sdXRpb25zIAoKCgpgYGAKCmBgYHtyfQojIGMoMC4xLDAuMjUsMC41LDAuNzUsMSkKcmVzb2x1dGlvbnMgPSBjKCJSTkFfc25uX3Jlcy4wLjEiLCJSTkFfc25uX3Jlcy4wLjI1IiwiUk5BX3Nubl9yZXMuMC41IiwiUk5BX3Nubl9yZXMuMC43NSIsIlJOQV9zbm5fcmVzLjEiKQoKZm9yIChyZXMgaW4gcmVzb2x1dGlvbnMpewogcHJpbnQoRG9IZWF0bWFwKHNldSwgZmVhdHVyZXMgPSBhbGxBQiwgZ3JvdXAuYnkgPSByZXMpKQogCn0KCmBgYAoKYGBge3J9CiMgdHJ5IHRoZSBkb3RwbG90IGFnYWluCiMgYygwLjEsMC4yNSwwLjUsMC43NSwxKQpyZXNvbHV0aW9ucyA9IGMoIlJOQV9zbm5fcmVzLjAuMSIsIlJOQV9zbm5fcmVzLjAuMjUiLCJSTkFfc25uX3Jlcy4wLjUiLCJSTkFfc25uX3Jlcy4wLjc1IiwiUk5BX3Nubl9yZXMuMSIpCgpmb3IgKHJlcyBpbiByZXNvbHV0aW9ucyl7CiBwcmludChEb3RQbG90KHNldSwgZmVhdHVyZXMgPSBhbGxBQiwgZ3JvdXAuYnkgPSByZXMsIGNvbHMgPSBjKCJibHVlIiwicmVkIikpKQogCn0KCiMgd2hhdCBpcyBoYXBwZW5pbmc/CgpgYGAKClRyeSBmZWF0dXJlIG1hcHMKCmBgYHtyfQoKCmZvciAoQUIgaW4gYWxsQUIpewogcHJpbnQoRmVhdHVyZVBsb3Qoc2V1LCBmZWF0dXJlcyA9IEFCKSxzbG90ID0gJ3NjYWxlLmRhdGEnLG1pbi5jdXRvZmYgPSAncTEwJywgbWF4LmN1dG9mZiA9JzkwJykKIAp9CgojIGF1dG90aHJlc2hvbGQgaXNuJ3QgZ3JlYXQgLSBJIHNldCBxdWFydGlsZXMgCgojRmVhdHVyZVBsb3Qoc2V1LCBmZWF0dXJlcyA9ICJPNCIsIG1pbi5jdXRvZmYgPSAxLCBtYXguY3V0b2ZmID0gMjUpCiNGZWF0dXJlUGxvdChzZXUsIGZlYXR1cmVzID0gIk80Iiwgc2xvdCA9ICdzY2FsZS5kYXRhJyxtaW4uY3V0b2ZmID0gMC4xLCBtYXguY3V0b2ZmID0gMjUpCgojRmVhdHVyZVBsb3Qoc2V1LCBmZWF0dXJlcyA9ICJPNCIsIHNsb3QgPSAnc2NhbGUuZGF0YScsbWluLmN1dG9mZiA9ICdxNScsIG1heC5jdXRvZmYgPSc5OScpCgoKCgpgYGAKClRyeSByaWRnZSBwbG90cwoKYGBge3J9ClJpZGdlUGxvdChzZXUsIGZlYXR1cmVzID0gYygiQ0Q1NiIsIkNEMjQiKSwgbmNvbCA9IDIpICMgbWlzc2luZyB2YWx1ZXM/Pz8KVmxuUGxvdChzZXUsIGZlYXR1cmVzID0gYygiQ0Q1NiIsIkNEMjQiKSwgbmNvbCA9IDIpICMgYWxzbyBtaXNzaW5nIHZhbHVlcz8/PwoKYGBgCgoKSSdsbCBzYXZlIHRoZSBzZXVyYXQgb2JqZWN0IGFuZCBzZWUgYWJvdXQgdHJ5aW5nIHRvIGNsdXN0ZXIgd2l0aCBwaGVub2dyYXBoLiAgSSBiZWxpZXZlIEkgaGF2ZSByZWFkIGluIHRoZSB1bmFsbGlnbmVkIG5vdCBub3JtYWxpemVkIGRhdGEgCgoKYGBge3J9CnNhdmVSRFMoc2V1LCAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvRmxvd0N5dG9tZXRyeS9QaGVub0lEL0FuYWx5c2lzLzlNQk8vcHJlcHJvX291dHNqYW4yMC05MDAwY2VsbHMvU2V1cmF0ZnJvbUZsb3dzb20ucmRzIikKCgoKYGBgCgoKUGhlbm9ncmFwaCBjbHVzdGVyaW5nIHVzZXMgSmFjY2FyZCBjb2VmZmljaWVudCBiZXR3ZWVuIG5lcnVlc3QgbmVpZ2JvcnMgc2V0cyBhbmQgdGhlbiBpZCBjb21tdW5pdGllcyBieSBsb3V2YWluIAoKCmBgYHtyfQojIGluc3RhbGwKaWYoIXJlcXVpcmUoZGV2dG9vbHMpKXsKICBpbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpICMgSWYgbm90IGFscmVhZHkgaW5zdGFsbGVkCn0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJKaW5taWFvQ2hlbkxhYi9ScGhlbm9ncmFwaCIpCgpsaWJyYXJ5KFJwaGVub2dyYXBoKQoKYGBgCgoKCgo=